package com.sevntu.apvs.lab3;

import java.util.LinkedList;
import java.util.List;

public class ElementSet {

	private int[][] netMatrix = null;
	private int netMatrixLine = -1;
	private int netMatrixColumn = -1;
	private int elemMatrixLine = -1;
	private int elemMatrixColumn = -1;

	private List<Double> netMatrixIndex = new LinkedList<Double>();
	private List<LinkedList<Integer>> netMatrixContent = new LinkedList<LinkedList<Integer>>();
	private List<String> elemSet = new LinkedList<String>();
	private List<Integer> rang = new LinkedList<Integer>();
	private int setCount = 0;

	public ElementSet(final int[][] netMatrix, final int netMatrixLine, final int netMatrixColumn,
			final int elemMatrixLine, final int elemMatrixColumn) {
		this.netMatrix = netMatrix;
		this.netMatrixLine = netMatrixLine;
		this.netMatrixColumn = netMatrixColumn;
		this.elemMatrixLine = elemMatrixLine;
		this.elemMatrixColumn = elemMatrixColumn;
		getRang();
		buildElemSet();
	}

	public List<String> getElemSet() {
		return elemSet;
	}

	private void getRang() {
		int countRang = 0;

		for (int i = 0; i < netMatrixLine; i++) {
			LinkedList<Integer> matrixContent = new LinkedList<Integer>();
			for (int j = 0; j < netMatrixColumn; j++) {
				if (netMatrix[i][j] == 1) {
					countRang++;
					matrixContent.add(j + 1);
				}
			}
			if (countRang > elemMatrixColumn) {
				parseNetMatrixLine(i + 1, matrixContent, countRang);
			} else {
				netMatrixIndex.add((double) i + 1);
				netMatrixContent.add(matrixContent);
				rang.add(countRang);
			}
			countRang = 0;
		}
	}

	private void parseNetMatrixLine(final double index, LinkedList<Integer> matrixContent, final int countRang) {
		int tempRang = countRang;
		int partRang = -1;
		double partIndex = 0.1;
		LinkedList<Integer> tempMatrix = new LinkedList<Integer>();
		int matrixIndex = 0;

		while (true) {
			netMatrixIndex.add(index + partIndex);
			partRang = tempRang % elemMatrixColumn;

			if (partRang == 0) {
				partRang = tempRang / (tempRang / elemMatrixColumn);
			}

			rang.add(partRang);

			for (int i = 0; i < partRang; i++, matrixIndex++) {
				tempMatrix.add((Integer) matrixContent.get(matrixIndex));
			}

			netMatrixContent.add(tempMatrix);
			tempMatrix = new LinkedList<Integer>();
			tempRang = tempRang - partRang;
			partIndex += 0.1;

			if (tempRang <= elemMatrixColumn) {
				netMatrixIndex.add(index + partIndex);

				for (int i = 0; i < tempRang; i++, matrixIndex++) {
					tempMatrix.add((Integer) matrixContent.get(matrixIndex));
				}

				netMatrixContent.add(tempMatrix);

				rang.add(tempRang);
				break;
			}
		}
	}

	private void buildElemSet() {
		LinkedList<Integer> tempSet = null;
		LinkedList<Integer> generalVars = null;
		LinkedList<Integer> newVars = null;
		int maxIndex = -1;

		while ((maxIndex = getMaxRangIndex()) >= 0) {
			setCount++;
			tempSet = (LinkedList<Integer>) netMatrixContent.get(maxIndex).clone();
			int setLines = 2;
			addToElemSet(netMatrixIndex.get(maxIndex), tempSet, true);

			while (tempSet.size() <= elemMatrixColumn && setLines <= elemMatrixLine) {
				int maxGeneral = 0;
				int minNew = 0;
				int nextIndex = -1;

				generalVars = getGeneralVars(tempSet);
				newVars = getNewVars(generalVars);

				for (int i = 0; i < rang.size(); i++) {
					if (null != rang.get(i) && (generalVars.get(i) > maxGeneral)) {
						if ((newVars.get(i) + tempSet.size()) > elemMatrixColumn)
							continue;
						maxGeneral = generalVars.get(i);
						minNew = newVars.get(i);
						nextIndex = i;
					}
				}
				if (maxGeneral == 0) {
					for (int i = 0; i < rang.size(); i++) {
						if (null != rang.get(i) && ((newVars.get(i) + tempSet.size()) <= elemMatrixColumn)
								&& newVars.get(i) > minNew) {
							maxGeneral = generalVars.get(i);
							minNew = newVars.get(i);
							nextIndex = i;
						}
					}
				}

				if (nextIndex == -1) break; 
				rang.set(nextIndex, null);

				LinkedList<Integer> nextTempSet = (LinkedList<Integer>) netMatrixContent.get(nextIndex).clone();
				for (int i = 0; i < nextTempSet.size(); i++) {
					boolean isEqual = true;

					for (int j = 0; j < tempSet.size(); j++) {
						if (nextTempSet.get(i) == tempSet.get(j))
							isEqual = false;
					}
					if (isEqual) {
						tempSet.add(nextTempSet.get(i));
					}
				}
				setLines++;
				addToElemSet(netMatrixIndex.get(nextIndex), tempSet, false);
			}
		}
	}

	private void addToElemSet(double line, LinkedList<Integer> tempSet, boolean isSet) {
		StringBuilder concat = new StringBuilder();
		if (isSet) {
			concat.append("===== M" + setCount + ": =====\n");
		}
		if (line - (int) line != 0)
			concat.append(line + " [");
		else
			concat.append((int) line + " [");

		for (int i = 0; i < tempSet.size(); i++) {
			concat.append(decodeToSymbol(tempSet.get(i)));
		}
		concat.append("]\n");
		elemSet.add(concat.toString());
	}

	private String decodeToSymbol(int value) {
		String symbol = "";

		switch (value) {
		case 1:
			symbol = "a";
			break;
		case 2:
			symbol = "b";
			break;
		case 3:
			symbol = "c";
			break;
		case 4:
			symbol = "d";
			break;
		case 5:
			symbol = "e";
			break;
		case 6:
			symbol = "f";
			break;
		case 7:
			symbol = "g";
			break;
		case 8:
			symbol = "h";
			break;
		case 9:
			symbol = "i";
			break;
		case 10:
			symbol = "j";
			break;
		case 11:
			symbol = "k";
			break;
		case 12:
			symbol = "l";
			break;
		case 13:
			symbol = "m";
			break;
		case 14:
			symbol = "n";
			break;
		case 15:
			symbol = "o";
			break;
		case 16:
			symbol = "p";
			break;
		}
		return symbol;
	}

	private LinkedList<Integer> getGeneralVars(LinkedList<Integer> tempSet) {
		int general = 0;
		LinkedList<Integer> generalVars = new LinkedList<Integer>();

		for (int i = 0; i < rang.size(); i++) {
			if (rang.get(i) == null) {
				generalVars.add(null);
				continue;
			}

			for (int j = 0; j < tempSet.size(); j++) {
				for (int k = 0; k < netMatrixContent.get(i).size(); k++) {
					if (tempSet.get(j) == netMatrixContent.get(i).get(k)) {
						general++;
					}
				}
			}
			generalVars.add(general);
			general = 0;
		}

		return generalVars;
	}

	private LinkedList<Integer> getNewVars(LinkedList<Integer> generalVars) {
		LinkedList<Integer> newVars = new LinkedList<Integer>();

		for (int i = 0; i < rang.size(); i++) {
			if (rang.get(i) == null) {
				newVars.add(null);
				continue;
			}
			newVars.add(netMatrixContent.get(i).size() - generalVars.get(i));
		}

		return newVars;
	}

	private int getMaxRangIndex() {
		double maxRang = -1;
		int maxIndex = -1;

		for (int i = 0; i < rang.size(); i++) {
			if (null != rang.get(i) && maxRang < rang.get(i)) {
				maxRang = rang.get(i);
				maxIndex = i;
			}
		}
		if (maxIndex != -1) {
			rang.set(maxIndex, null);
		}
		return maxIndex;
	}

}
